package com.scud.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.scud.client.product.IProductClient;
import com.scud.common.constants.RabbitmqConstants;
import com.scud.common.constants.RedisConstants;
import com.scud.common.error.ErrorConstants;
import com.scud.common.exception.ScudException;
import com.scud.model.PageDto;
import com.scud.model.PageResult;
import com.scud.model.order.dto.CreateOrderDto;
import com.scud.model.order.dto.FullOrderDto;
import com.scud.model.order.pojo.Order;
import com.scud.model.order.pojo.OrderDetails;
import com.scud.model.order.vo.ManageOrderVo;
import com.scud.model.order.vo.OrderInfoVo;
import com.scud.model.order.vo.OrderSkuVo;
import com.scud.model.system.pojo.User;
import com.scud.order.mapper.OrderDetailsMapper;
import com.scud.order.mapper.OrderMapper;
import com.scud.order.service.OrderService;
import com.scud.order.util.OrderIdWorker;
import com.scud.order.util.SecurityUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailsMapper orderDetailsMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private OrderIdWorker orderIdWorker;

    @Autowired
    private IProductClient productClient;

    /**
     * 创建订单
     *
     * @param createOrderDto
     * @return
     */
    @Override
    public String createOrder(CreateOrderDto createOrderDto) {
        List<Long> skuIds = createOrderDto.getSkuIds();
        for (Long skuId : skuIds) {
            RLock lock = redissonClient.getLock(RedisConstants.LOCK_SKU_PREFIX + skuId);
            boolean tryLock;
            try {
                tryLock = lock.tryLock(3, 10, TimeUnit.SECONDS);
                if (tryLock) {
                    //1.判断库存
                    if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(RedisConstants.SKU + skuId))) {
                        throw new ScudException(ErrorConstants.STOCK_EMPTY);
                    }
                    String stockStr = stringRedisTemplate.opsForValue()
                            .get(RedisConstants.SKU + skuId);
                    int stock = Integer.parseInt(Objects.requireNonNull(stockStr));
                    if (stock < 1) {
                        throw new ScudException(ErrorConstants.STOCK_EMPTY);
                    }
                    //2.扣减redis库存
                    stringRedisTemplate.opsForValue()
                            .increment(RedisConstants.SKU + skuId, -1);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
        }

        //3.创建订单和订单详情
        long l = orderIdWorker.generateOrderId();
        User user = SecurityUtil.getUser();
        Long userId = user.getId();
        //3.1构造总体order对象
        Order order = Order.builder().id(l).userId(userId)
                .status(0).address(createOrderDto.getAddress()).
                receiverPhone(createOrderDto.getReceiverPhone()).
                receiverName(createOrderDto.getReceiverName()).build();
        List<OrderDetails> orderDetails = new ArrayList<>();
        Map<Long, Integer> skuCount = new HashMap<>();
        //3.2统计各个sku的个数
        for (Long skuId : skuIds) {
            skuCount.merge(skuId, 1, Integer::sum);
        }
        //3.3构造订单详情集合
        Set<Map.Entry<Long, Integer>> entrySet = skuCount.entrySet();
        for (Map.Entry<Long, Integer> entry : entrySet) {
            OrderDetails build = OrderDetails.builder().orderId(l)
                    .skuId(entry.getKey()).count(entry.getValue()).build();
            orderDetails.add(build);
        }

        FullOrderDto fullOrderDto = new FullOrderDto(order, orderDetails, skuCount);
        String fullOrderStr = JSON.toJSONString(fullOrderDto);

        //4.发送mq消息
        //4.1 通知product子系统执行数据库操作
        rabbitTemplate.convertAndSend(RabbitmqConstants.SCUD_ORDER_EXCHANGE,
                RabbitmqConstants.SCUD_ORDER_KEY, fullOrderStr);
        //4.2 实现超时自动取消
        rabbitTemplate.convertAndSend(RabbitmqConstants.SCUD_ORDER_OVER_EXCHANGE,
                RabbitmqConstants.SCUD_ORDER_OVER_KEY, fullOrderStr);
        return String.valueOf(l);
    }

    @Override
    public OrderInfoVo getOrderIndo(Long orderId) {
        //在数据库中查询订单信息
        Order order = orderMapper.selectById(orderId);
        if (order==null){
            return null;
        }
        //查询订单的商品详细信息
        List<OrderDetails> orderDetailsList = orderDetailsMapper.selectByOrderId(orderId);
        if (orderDetailsList == null || orderDetailsList.isEmpty()) {
            return null;
        }
        //进行feign的远程调用，获取到商品的相关规格信息
        List<OrderSkuVo> goods = productClient.getOrderSkuVoList(orderDetailsList);
        //封装返回对象
        OrderInfoVo vo = new OrderInfoVo();
        BeanUtils.copyProperties(order, vo);
        vo.setGoods(goods);
        return vo;
    }

    /**
     * feign远程调用,在数据库中创建订单
     *
     * @param orderDto
     */
    @Override
    @Transactional
    public void saveAOrder(FullOrderDto orderDto) {
        orderDto.getOrder().setCreateTime(LocalDateTime.now());
        orderDto.getOrder().setOverTime(LocalDateTime.now().
                plus(RabbitmqConstants.DELAY_TIME, ChronoUnit.MILLIS));
        orderMapper.addOrder(orderDto.getOrder());
        orderDetailsMapper.addOrderDetails(orderDto.getOrderDetails());
    }

    @Override
    public Order getOrder(Long id) {
        return orderMapper.selectById(id);
    }

    /**
     * 更改订单状态
     * @param id
     * @param status
     */
    @Override
    public void updateOrderStatus(Long id, int status) {
        orderMapper.updateOrderStatus(id, status);
    }

    /**
     * 获取当前用户的订单列表
     * @param :
     * @return Result
     * @author vermouth
     */
    @Override
    public List<Order> getCurrentOrderList() {
        //获取当前用户的id
        Long userId = SecurityUtil.getUser().getId();
        //根据用户id查询订单列表
        List<Order> orderList=orderMapper.selectByUserId(userId);
        return orderList;
    }

    /**
     * 模拟支付功能
     * @param orderId:
     * @return Result
     * @author vermouth
     */
    @Override
    @Transactional
    public void payOrder(Long orderId) {
        //校验订单状态，这里方便测试不做校验
        Order order = orderMapper.selectById(orderId);
//        if (!order.getStatus().equals(0)){
//            throw new ScudException(ErrorConstants.ORDER_STATUS_ERROR);
//        }
        //修改订单状态为支付状态
        orderMapper.updateOrderStatus(orderId,1);
        //设置支付时间
        orderMapper.setPayTime(LocalDateTime.now(),orderId);
    }

    /**
     * 商家查询相关订单信息
     * @param dto:
     * @return Result
     * @author vermouth
     */
    @Override
    public PageResult<ManageOrderVo> manageOrderPage(PageDto dto) {
        //获取当前用户id
        Long userId = SecurityUtil.getUser().getId();
        //按照条件查询订单详情数据
        dto.setPage((dto.getPage()-1)*dto.getPageSize());
        Long total=orderDetailsMapper.countByUserId(userId);
        List<ManageOrderVo> orderDetailsList=orderDetailsMapper.selectByManageQuery(dto,userId);
        //封装数据
        List<ManageOrderVo> list=productClient.getManageOrderPage(orderDetailsList);
        PageResult<ManageOrderVo> pageResult = new PageResult<>();
        pageResult.setTotal(total);
        pageResult.setList(list);
        return pageResult;
    }
}
